﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;

namespace HodhodNews.Extraction
{
    public class SimpleNewsExtractionProvider : NewsExtractionProvider
    {
        const string textTag = "#text";
        HtmlAgilityPack.HtmlDocument doc;

        List<ArtilceNode> statisticsList;
        string[] tags = new string[] { "div", "table", "td", "p", "span" };

        private string[] HumanText { get; set; }

        Dictionary<Guid, ArtilceNode> LeafList;
        public string Summary { get; private set; }
        private void PrepareParser(string newsSource)
        {
            statisticsList = new List<ArtilceNode>();
            LeafList = new Dictionary<Guid, ArtilceNode>();
            doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(newsSource);

        }

        private bool CanReadStatisticsFrom(HtmlNode nd)
        {
            if (nd.Name != "#text")
                return false;
            if (nd.ParentNode.Name == "select")
                return false;
            if (nd.InnerHtml.Length == 0)
                return false;
            if (nd.Name == "script" | nd.Name == "form" | nd.Name == "body")
                return false;
            HtmlNode pn = nd.ParentNode;
            if (pn.Name == "script" | pn.Name == "form" | pn.Name == "body")
                return false;
            if (nd.Name == "#text" & (pn.Name == "table" | pn.Name == "tr"))
                return false;
            return true;
        }
        private static int GetNumberOfOccurance(HtmlNode node, string tag)
        {
            HtmlNodeCollection nodes = node.SelectNodes("//" + tag);
            return nodes == null ? 0 : nodes.Count;
        }
        string Encode(string text)
        {
            string encodedString = "";
            foreach (char c in text)
            {
                encodedString += "&#" + Convert.ToInt16(c) + ";";
            }
            return encodedString;
        }
        private bool ContainsHumanText(HtmlNode wn)
        {
            bool yes = wn.InnerText.Contains("ا") | wn.InnerText.Contains(Encode("ا"));
            if (yes)//الهدف توفير البحث في حالة الوصول للنتيجة
                return true;
            yes = wn.InnerText.Contains("أ") | wn.InnerText.Contains(Encode("أ"));
            if (yes)
                return true;
            yes = wn.InnerText.Contains("و") | wn.InnerText.Contains(Encode("و"));
            if (yes)
                return true;
            yes = wn.InnerText.Contains("ي") | wn.InnerText.Contains(Encode("ي"));

            return yes;
        }
        private ArtilceNode ReadStatisticsFrom(HtmlNode node)
        {
            ArtilceNode stat = new ArtilceNode() { Ignore = true };

            stat.CharactersLength = node.InnerText.Trim().Length;
            if (node.ChildNodes.Count > 15)
                return stat;
            stat.NodeName = node.Name;
            stat.Links = GetNumberOfOccurance(node, "a");
            stat.Images = GetNumberOfOccurance(node, "img");
            stat.ConfidenceLevel = 1;
            if (stat.CharactersLength <= 100)
                return stat;
            if (stat.Images / stat.CharactersLength <= .01 & stat.Links / stat.CharactersLength <= .01)
            {
                if (ContainsHumanText(node))
                {
                    stat.Ignore = false;
                    stat.HtmlNode = node;
                }
            }
            return stat;
        }
        private HtmlNode GetParent(HtmlNode node)
        {
            HtmlNode nd = node.ParentNode;
            while (!nd.Name.EqualsAny(tags))
            {
                nd = nd.ParentNode;
            }
            return nd;
        }
        private string Parse(HtmlNode node)
        {
            //string ret = "-" + node.Name;
            if (node.ChildNodes.Count == 0)
                return "leaf";

            foreach (HtmlNode childNode in node.ChildNodes)
            {
                string tag = Parse(childNode);

                if (tag != "leaf")
                {
                    continue;
                }
                if (!CanReadStatisticsFrom(childNode))
                {
                    continue;
                }
                ArtilceNode statistics = ReadStatisticsFrom(childNode);

                if (statistics.Ignore)
                {
                    continue;
                }

                statisticsList.Add(statistics);
                HtmlNode parent = GetParent(statistics.HtmlNode);
                if (LeafList.ContainsKey(parent.OId))
                {
                    LeafList[parent.OId].ConfidenceLevel++;
                }
                else
                {
                    LeafList.Add(parent.OId, statistics);
                }
            }
            return "";
        }

        ExtractedNews ExtractFrom(int redundancy, HtmlNode node)
        {
            if (redundancy >= 3)
            {
                if (IsNewsArticle(node))
                {
                    return new ExtractedNews()
                    {
                        Text = node.InnerHtml
                    };
                }
            }
            return null;
        }

        HtmlNode Find(HtmlNode parent, string text)
        {
            foreach (var n in parent.ChildNodes)
            {
                if (n.InnerText.Contains(text))
                {
                    return n;
                }
                var found = Find(n, text);
                if (found != null)
                {
                    return found;
                }               
            }
            return null;
        }
        public override ExtractedNews Extract(ExtracationOptions options)
        {
            ExtractedNews extractedNews = null;
            Initialize(options);

            var node = Find( doc.DocumentNode.SelectNodes("//body")[0],options.NewsTitle);

            return null;
        }

        private ArtilceNode GetMostRedundatParent(List<ArtilceNode> list)
        {
            ArtilceNode ps = null;
            Dictionary<Guid, ArtilceNode> parents = new Dictionary<Guid, ArtilceNode>();
            foreach (ArtilceNode stat in list)
            {
                HtmlNode parent = GetParent(stat.HtmlNode);
                if (parents.ContainsKey(parent.OId))
                    ps = parents[parent.OId];
                else
                {
                    ps = new ArtilceNode();
                    ps.HtmlNode = parent;
                    parents.Add(ps.HtmlNode.OId, ps);
                }
                ps.ConfidenceLevel++;
            }
            return GetMostRedundant(parents.Values.ToList());
        }

        private void Initialize(ExtracationOptions options)
        {
            this.Summary = options.NewsSummaryText;

            this.HumanText = new string[] { "ا", 
                "أ", 
                "و", 
                "ي", 
                this.Summary.TrySubstractWords(0,7) };

            PrepareParser(options.NewsUrl);
        }

        private ArtilceNode GetMostRedundat2LevelParent(List<ArtilceNode> list, out List<ArtilceNode> parentsList)
        {
            var parents = new Dictionary<Guid, ArtilceNode>();
            ArtilceNode ps = null;
            ArtilceNode redundantParent = new ArtilceNode()
            {
                ConfidenceLevel = 1,
                HtmlNode = GetParent(GetParent(statisticsList[0].HtmlNode))
            };
            parents.Add(redundantParent.HtmlNode.OId, redundantParent);

            foreach (ArtilceNode stat in LeafList.Values)
            {
                HtmlNode parent = GetParent(GetParent(stat.HtmlNode));
                if (parents.ContainsKey(parent.OId))
                {
                    ps = parents[parent.OId];
                }
                else
                {
                    ps = new ArtilceNode();
                    ps.HtmlNode = parent;
                    parents.Add(ps.HtmlNode.OId, ps);
                }
                ps.ConfidenceLevel++;
            }

            redundantParent = GetMostRedundant(parents.Values.ToList());
            parentsList = parents.Values.ToList();

            return redundantParent;
        }
        private ArtilceNode GetMostRedundat1LevelParent(List<ArtilceNode> list, ArtilceNode redundantParent)
        {
            var parents = new Dictionary<Guid, ArtilceNode>();
            ArtilceNode ps = null;

            foreach (ArtilceNode stat in LeafList.Values)
            {
                HtmlNode parent = GetParent(stat.HtmlNode);
                if (parents.ContainsKey(parent.OId))
                {
                    ps = parents[parent.OId];
                }
                else
                {
                    ps = new ArtilceNode();
                    ps.HtmlNode = parent;
                    parents.Add(ps.HtmlNode.OId, ps);
                }
                ps.ConfidenceLevel++;
            }

            redundantParent = GetMostRedundant(parents.Values.ToList(),redundantParent);
            return redundantParent;
        }
        ArtilceNode GetMostRedundant(List<ArtilceNode> list)
        {
            if (list.Count == 0)
            {
                return null;
            }

            var redundantLeaf = list.First();

            foreach (ArtilceNode stat in list)
            {
                if (redundantLeaf.ConfidenceLevel < stat.ConfidenceLevel)
                    redundantLeaf = stat;
            }
            return redundantLeaf;
        }
        ArtilceNode GetMostRedundant(List<ArtilceNode> list, ArtilceNode compareWith)
        {
            if (list.Count == 0)
            {
                return null;
            }

            var redundantLeaf = compareWith;

            foreach (ArtilceNode stat in list)
            {
                if (redundantLeaf.ConfidenceLevel < stat.ConfidenceLevel)
                    redundantLeaf = stat;
            }
            return redundantLeaf;
        }
        private bool IsNewsArticle(HtmlNode node)
        {
            bool isNewsArticle = false;

            int charactersLength = node.InnerText.Length;


            if (1.0 * charactersLength / charactersLength < 0.5)
                return isNewsArticle;

            int linksCount = GetNumberOfOccurance(node, "a");
            int ImagesCount = GetNumberOfOccurance(node, "img");

            if (charactersLength > Paramaters.ArticleLength &
                ImagesCount / charactersLength <= Paramaters.ImagesToCharactersPercentage &
                linksCount / charactersLength <= Paramaters.LinksToCharactersPercentage)
            {
                isNewsArticle = true;
                //stat.Text = node.InnerText;
            }
            return isNewsArticle;
        }
    }
}
